//
// Created by kidon on 20-9-4.
//

#include "video_encoder.h"
#include "codec_utils.h"
#include "logger.h"
#include "error_code.h"

#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>

/******************************** 私有 ********************************/

#define TAG "Encoder"

// 图像缩放算法
#define DEF_VIDEO_SWS_SCALE SWS_FAST_BILINEAR

/** FFmpeg工具 */
typedef struct FFmpegTools {
    AVCodecContext *codec_ctx;
    AVCodec *codec;
    /** 临时数据帧，用于图像转换 */
    AVFrame *temp_frame;
    /** 输入原始数据 */
    AVFrame *input_frame;
    /** 输出已编码数据 */
    AVPacket *output_packet;
    /** 图像转换 */
    struct SwsContext *sws_ctx;
    /*** 源图像格式 */
    enum AVPixelFormat src_pix_fmt;
    /*** 目标图像格式 */
    enum AVPixelFormat dest_pix_fmt;
    /*** 过滤器输出 */
    AVFilterContext *buffersink_ctx;
    /*** 过滤器输入 */
    AVFilterContext *buffersrc_ctx;
    /*** 过滤器图 */
    AVFilterGraph *filter_graph;
    /** 过滤后的数据帧 */
    AVFrame *filter_frame;
} FFmpegTools;

static void release_ffmpeg(FFmpegTools *ffmpeg) {
    if (!ffmpeg) {
        return;
    }
    // 释放过滤器
    avfilter_graph_free(&(ffmpeg->filter_graph));
    av_frame_free(&(ffmpeg->filter_frame));
    // 释放编码器
    avcodec_free_context(&(ffmpeg->codec_ctx));
    av_frame_free(&(ffmpeg->temp_frame));
    av_frame_free(&(ffmpeg->input_frame));
    av_packet_free(&(ffmpeg->output_packet));
    // free SwsContext
    sws_freeContext(ffmpeg->sws_ctx);
    ffmpeg->sws_ctx = NULL;
    free(ffmpeg);
}

/*** 初始化过滤器 */
static int init_filters(TcVideoEncoder *encoder) {
    FFmpegTools *ffmpeg = (FFmpegTools *) encoder->extra;
    if (encoder->src_width == encoder->dest_width && encoder->src_height == encoder->dest_height) {
        // 不需要过滤
        ffmpeg->buffersrc_ctx = NULL;
        ffmpeg->buffersink_ctx = NULL;
        ffmpeg->filter_graph = NULL;
        return 0;
    }
    // 初始化数据帧
    ffmpeg->filter_frame = av_frame_alloc();
    if (!(ffmpeg->filter_frame)) {
        LOGE(TAG, "init_filters: 分配过滤数据帧失败\n");
        return -1;
    }
    // 组装过滤参数
    char filters_descr[512];
    int scale_w, scale_h, crop_w, crop_h, crop_x, crop_y;
    float src_size_rate = (float) (encoder->src_width) / (float) (encoder->src_height);
    float dest_size_rate = (float) (encoder->dest_width) / (float) (encoder->dest_height);
    if (src_size_rate > dest_size_rate) {
        // 源视频宽高比大于输出视频的宽高比
        scale_w = encoder->src_width * encoder->dest_height / encoder->src_height;
        scale_h = encoder->dest_height;
        crop_w = encoder->dest_width;
        crop_h = encoder->dest_height;
        // 组装参数
        switch (encoder->crop_type) {
            case CENTER:
                snprintf(filters_descr, sizeof(filters_descr),
                         "scale=%d:%d,crop=%d:%d",
                         scale_w, scale_h, crop_w, crop_h);
                break;
            case START:
                crop_x = 0;
                crop_y = 0;
                snprintf(filters_descr, sizeof(filters_descr),
                         "scale=%d:%d,crop=%d:%d:%d,%d",
                         scale_w, scale_h, crop_w, crop_h, crop_x, crop_y);
                break;
            case END:
                crop_x = scale_w - crop_w;
                crop_y = 0;
                snprintf(filters_descr, sizeof(filters_descr),
                         "scale=%d:%d,crop=%d:%d:%d,%d",
                         scale_w, scale_h, crop_w, crop_h, crop_x, crop_y);
                break;
        }
    } else if (src_size_rate < dest_size_rate) {
        // 源视频宽高比小于输出视频的宽高比
        scale_w = encoder->dest_width;
        scale_h = encoder->src_height * encoder->dest_width / encoder->src_width;
        crop_w = encoder->dest_width;
        crop_h = encoder->dest_height;
        // 组装参数
        switch (encoder->crop_type) {
            case CENTER:
                snprintf(filters_descr, sizeof(filters_descr),
                         "scale=%d:%d,crop=%d:%d",
                         scale_w, scale_h, crop_w, crop_h);
                break;
            case START:
                crop_x = 0;
                crop_y = 0;
                snprintf(filters_descr, sizeof(filters_descr),
                         "scale=%d:%d,crop=%d:%d:%d,%d",
                         scale_w, scale_h, crop_w, crop_h, crop_x, crop_y);
                break;
            case END:
                crop_x = 0;
                crop_y = scale_h - crop_h;
                snprintf(filters_descr, sizeof(filters_descr),
                         "scale=%d:%d,crop=%d:%d:%d,%d",
                         scale_w, scale_h, crop_w, crop_h, crop_x, crop_y);
                break;
        }
    } else {
        // 比率相等，不需要裁剪
        scale_w = encoder->dest_width;
        scale_h = encoder->dest_height;
        // 组装参数
        snprintf(filters_descr, sizeof(filters_descr),
                 "scale=%d:%d",
                 scale_w, scale_h);
    }
    // 初始化过滤器
    char args[512];
    int ret = 0;
    const AVFilter *buffersrc = avfilter_get_by_name("buffer");
    const AVFilter *buffersink = avfilter_get_by_name("buffersink");
    AVFilterInOut *outputs = avfilter_inout_alloc();
    AVFilterInOut *inputs = avfilter_inout_alloc();
    AVRational time_base = ffmpeg->codec_ctx->time_base;
    enum AVPixelFormat pix_fmts[2];
    if (ffmpeg->codec_ctx->codec_id == AV_CODEC_ID_MJPEG) {
        pix_fmts[0] = AV_PIX_FMT_YUVJ420P;
    } else {
        pix_fmts[0] = AV_PIX_FMT_YUV420P;
    }
    pix_fmts[1] = AV_PIX_FMT_NONE;

    ffmpeg->filter_graph = avfilter_graph_alloc();
    if (!outputs || !inputs || !(ffmpeg->filter_graph)) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    /* buffer video source: the decoded frames from the decoder will be inserted here. */
    snprintf(args, sizeof(args),
             "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
             encoder->src_width, encoder->src_height, ffmpeg->codec_ctx->pix_fmt,
             time_base.num, time_base.den,
             ffmpeg->codec_ctx->sample_aspect_ratio.num,
             ffmpeg->codec_ctx->sample_aspect_ratio.den);

    LOGD(TAG, "init_filters args=%s\n", args);
    LOGD(TAG, "init_filters filters_descr=%s\n", filters_descr);

    ret = avfilter_graph_create_filter(&(ffmpeg->buffersrc_ctx), buffersrc, "in",
                                       args, NULL, ffmpeg->filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
        goto end;
    }

    /* buffer video sink: to terminate the filter chain. */
    ret = avfilter_graph_create_filter(&(ffmpeg->buffersink_ctx), buffersink, "out",
                                       NULL, NULL, ffmpeg->filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
        goto end;
    }

    ret = av_opt_set_int_list(ffmpeg->buffersink_ctx, "pix_fmts", pix_fmts,
                              AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
        goto end;
    }

    /*
     * Set the endpoints for the filter graph. The filter_graph will
     * be linked to the graph described by filters_descr.
     */

    /*
     * The buffer source output must be connected to the input pad of
     * the first filter described by filters_descr; since the first
     * filter input label is not specified, it is set to "in" by
     * default.
     */
    outputs->name = av_strdup("in");
    outputs->filter_ctx = ffmpeg->buffersrc_ctx;
    outputs->pad_idx = 0;
    outputs->next = NULL;

    /*
     * The buffer sink input must be connected to the output pad of
     * the last filter described by filters_descr; since the last
     * filter output label is not specified, it is set to "out" by
     * default.
     */
    inputs->name = av_strdup("out");
    inputs->filter_ctx = ffmpeg->buffersink_ctx;
    inputs->pad_idx = 0;
    inputs->next = NULL;

    if ((ret = avfilter_graph_parse_ptr(ffmpeg->filter_graph, filters_descr,
                                        &inputs, &outputs, NULL)) < 0)
        goto end;

    if ((ret = avfilter_graph_config(ffmpeg->filter_graph, NULL)) < 0)
        goto end;

    // 结束
    end:
    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);

    return ret;
}

/******************************** 公开 ********************************/

/**
 * 初始化视频编码器
 *
 * @param encoder           编码器结构体指针地址
 * @param codec_type        编码类型
 * @param src_width         源图像宽度
 * @param src_height        源图像高度
 * @param src_pix_fmt_type  源图像格式
 * @param dest_width        输出图像宽度
 * @param dest_height       输出图像高度
 * @param crop_type         裁剪类型
 * @param fps               帧率
 * @param bit_rate          码率
 * @return                  状态码
 */
int video_encoder_prepare(
        TcVideoEncoder **encoder,
        enum CodecType codec_type,
        int src_width,
        int src_height,
        enum PixelFormatType src_pix_fmt_type,
        int dest_width,
        int dest_height,
        enum ImageCropTypes crop_type,
        int fps,
        int bit_rate
) {
    int ret;
    char *msg;
    FFmpegTools *ffmpeg = NULL;

    // 判断参数是否正确
    const char *codec_name = get_codec_name(codec_type);
    if (codec_name == NULL || src_width <= 0 || src_height <= 0 || fps <= 0) {
        msg = "video_encoder_prepare: 参数错误\n";
        ret = ERROR_ARGUMENTS;
        goto error;
    }

    // 初始化编码器结构体
    (*encoder) = malloc(sizeof(TcVideoEncoder));
    memset(*encoder, 0, sizeof(TcVideoEncoder));

    (*encoder)->codec_type = codec_type;
    (*encoder)->src_width = src_width;
    (*encoder)->src_height = src_height;
    (*encoder)->src_pix_fmt_type = src_pix_fmt_type;
    (*encoder)->dest_width = dest_width;
    (*encoder)->dest_height = dest_height;
    (*encoder)->crop_type = crop_type;
    (*encoder)->fps = fps;
    (*encoder)->bit_rate = bit_rate;

    LOGD(TAG,
         "video_encoder_prepare codec=%s, src_width=%d, src_height=%d, dest_width=%d, dest_height=%d, crop_type=%d, pix_fmt=%d, fps=%d, bit_rate=%d\n",
         codec_name, src_width, src_height, src_pix_fmt_type,
         dest_width, dest_height, crop_type,
         fps, bit_rate
    );

    // 初始化FFmpeg
    (*encoder)->extra = malloc(sizeof(FFmpegTools));
    memset((*encoder)->extra, 0, sizeof(FFmpegTools));
    ffmpeg = (FFmpegTools *) (*encoder)->extra;
    // 初始化FFmpeg编码器
    ffmpeg->codec = avcodec_find_encoder_by_name(codec_name);
    if (!(ffmpeg->codec)) {
        msg = "video_encoder_prepare: 找不到编码器\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    ffmpeg->codec_ctx = avcodec_alloc_context3(ffmpeg->codec);
    if (!(ffmpeg->codec_ctx)) {
        msg = "video_encoder_prepare: 分配编码器空间失败\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    LOGD(TAG, "codec_ctx id=%d", ffmpeg->codec_ctx->codec_id);

    ffmpeg->codec_ctx->bit_rate = bit_rate;
    ffmpeg->codec_ctx->width = dest_width;
    ffmpeg->codec_ctx->height = dest_height;
    ffmpeg->codec_ctx->time_base = (AVRational) {1, fps};
    ffmpeg->codec_ctx->framerate = (AVRational) {fps, 1};
    ffmpeg->codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
    if (ffmpeg->codec_ctx->codec_id == AV_CODEC_ID_MJPEG) {
        // mjpeg只支持AV_PIX_FMT_YUVJ420P格式
        ffmpeg->dest_pix_fmt = AV_PIX_FMT_YUVJ420P;
        ffmpeg->codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
    } else {
        ffmpeg->dest_pix_fmt = AV_PIX_FMT_YUV420P;
        ffmpeg->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
    }
    // 将codec和codec_ctx关联一起
    ret = avcodec_open2(ffmpeg->codec_ctx, ffmpeg->codec, NULL);
    if (ret < 0) {
        LOGE(TAG, "video_encoder_prepare: avcodec_open2 error info=%s, ret=%d", av_err2str(ret),
             ret);
        msg = "video_encoder_prepare: avcodec_open2 fail\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    // 初始化过滤器
    ret = init_filters((*encoder));
    if (ret < 0) {
        msg = "video_encoder_prepare: 初始化过滤器失败\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    // 初始化临时数据帧和图像转换器
    if (src_pix_fmt_type == I420 || src_pix_fmt_type == YV12) {
        // 不需要转换图像
        ffmpeg->temp_frame = NULL;
        ffmpeg->sws_ctx = NULL;
        if (ffmpeg->codec_ctx->codec_id == AV_CODEC_ID_MJPEG) {
            ffmpeg->src_pix_fmt = AV_PIX_FMT_YUVJ420P;
        } else {
            ffmpeg->src_pix_fmt = AV_PIX_FMT_YUV420P;
        }
    } else {
        // 初始化临时数据帧
        ffmpeg->temp_frame = av_frame_alloc();
        if (!(ffmpeg->temp_frame)) {
            msg = "video_encoder_prepare: 分配临时数据帧失败\n";
            ret = ERROR_ENCODER;
            goto error;
        }
        switch (src_pix_fmt_type) {
            case NV12:
                ffmpeg->src_pix_fmt = AV_PIX_FMT_NV12;
                ffmpeg->temp_frame->format = AV_PIX_FMT_NV12;
                break;
            case NV21:
                ffmpeg->src_pix_fmt = AV_PIX_FMT_NV21;
                ffmpeg->temp_frame->format = AV_PIX_FMT_NV21;
                break;
            default:
                LOGE(TAG, "video_encoder_prepare:未知像素类型：%d\n", src_pix_fmt_type);
                msg = "video_encoder_prepare:分配临时数据帧失败\n";
                ret = ERROR_ENCODER;
                goto error;
        }
        ffmpeg->temp_frame->width = src_width;
        ffmpeg->temp_frame->height = src_height;
        ret = av_frame_get_buffer(ffmpeg->temp_frame, 0);
        if (ret < 0) {
            msg = "video_encoder_prepare: 分配临时数据帧空间失败\n";
            ret = ERROR_ENCODER;
            goto error;
        }
        // 初始化图像转换器，只转换像素格式，不转换尺寸
        ffmpeg->sws_ctx = sws_getContext(
                src_width, src_height, ffmpeg->src_pix_fmt,
                src_width, src_height, ffmpeg->dest_pix_fmt,
                DEF_VIDEO_SWS_SCALE, NULL, NULL, NULL
        );
        if (!ffmpeg->sws_ctx) {
            msg = "video_encoder_prepare: 初始化图像转换器失败\n";
            ret = ERROR_ENCODER;
            goto error;
        }
    }
    // 初始化输入数据帧
    ffmpeg->input_frame = av_frame_alloc();
    if (!(ffmpeg->input_frame)) {
        msg = "video_encoder_prepare: 分配输入数据帧失败\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    ffmpeg->input_frame->format = ffmpeg->codec_ctx->pix_fmt;
    ffmpeg->input_frame->width = src_width;
    ffmpeg->input_frame->height = src_height;
    ret = av_frame_get_buffer(ffmpeg->input_frame, 0);
    if (ret < 0) {
        msg = "video_encoder_prepare: 分配输入数据帧空间失败\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    // 输出已编码数据帧
    ffmpeg->output_packet = av_packet_alloc();
    if (!(ffmpeg->output_packet)) {
        msg = "video_encoder_prepare: 分配输出数据帧失败\n";
        ret = ERROR_ENCODER;
        goto error;
    }

    return SUCCESS;

    // 错误
    error:
    LOGE(TAG, "%s", msg);
    return ret;
}

/**
 * 将原始视频数据写入编码器
 *
 * @param encoder   编码器结构体指针
 * @param src_data  输入数据数组
 * @param offset    数组开始位置
 * @param len       数据长度
 * @return          状态码
 */
int video_encoder_write_frame(
        TcVideoEncoder *encoder,
        const uint8_t *src_data,
        int offset,
        int len
) {
    int ret;
    char *msg;

    FFmpegTools *ffmpeg = (FFmpegTools *) encoder->extra;
    AVFrame *frame = ffmpeg->input_frame;
    // 准备输入数据帧
    ret = av_frame_make_writable(frame);
    if (ret < 0) {
        LOGE(TAG, "video_encoder_write_frame: av_frame_make_writable error info=%s, ret=%d",
             av_err2str(ret),
             ret);
        msg = "video_encoder_write_frame: av_frame_make_writable failed\n";
        ret = ERROR_ENCODER;
        goto error;
    }

    // 对输入数据进行转换
    const uint8_t *data = src_data + offset;
    if (ffmpeg->temp_frame && ffmpeg->sws_ctx) {
        // 准备临时数据帧
        ret = av_frame_make_writable(ffmpeg->temp_frame);
        if (ret < 0) {
            msg = "video_encoder_write_frame: 准备临时数据帧 failed\n";
            ret = ERROR_ENCODER;
            goto error;
        }
        // 复制数据
        av_image_fill_arrays(
                ffmpeg->temp_frame->data, ffmpeg->temp_frame->linesize,
                data, ffmpeg->src_pix_fmt, encoder->src_width, encoder->src_height, 1
        );
        // 开始转换
        sws_scale(ffmpeg->sws_ctx,
                  (const uint8_t *const *) ffmpeg->temp_frame->data, ffmpeg->temp_frame->linesize,
                  0, ffmpeg->temp_frame->height,
                  frame->data, frame->linesize);
    } else {
        if (encoder->src_pix_fmt_type == YV12) {
            // ffmpeg不直接支持YV21格式，需要手动进行转换成I420
            // YV21与I420的区别只是U数据域与V数据域位置调换了
            int size = encoder->src_width * encoder->src_height;
            int part = size / 4;
            uint8_t tmp_arr[part];
            // 临时保存V数据域
            memcpy(tmp_arr, data + size, part);
            // 复制U数据域
            memcpy((void *) (data + size), data + size + part, part);
            // 复制V数据域
            memcpy((void *) (data + size + part), tmp_arr, part);
        }
        // 将数据复制到数据帧
        av_image_fill_arrays(
                frame->data, frame->linesize,
                data, ffmpeg->src_pix_fmt, encoder->src_width, encoder->src_height, 1
        );
    }

    // 设置帧展示时间
    frame->pts = encoder->encode_frame_index;
    encoder->encode_frame_index++;

    if (ffmpeg->filter_graph) {
        // 将数据传给过滤器
        /* push the decoded frame into the filtergraph */
        ret = av_buffersrc_add_frame_flags(
                ffmpeg->buffersrc_ctx,
                frame,
                AV_BUFFERSRC_FLAG_KEEP_REF
        );
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
            msg = "video_encoder_write_frame: Error while feeding the filtergraph\n";
            ret = ERROR_ENCODER;
            goto error;
        }
        /* pull filtered frames from the filtergraph */
        while (1) {
            ret = av_buffersink_get_frame(ffmpeg->buffersink_ctx, ffmpeg->filter_frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            }
            if (ret < 0) {
                msg = "video_encoder_write_frame: av_buffersink_get_frame fail\n";
                ret = ERROR_ENCODER;
                goto error;
            }
            // 将数据传给编码器
            ret = avcodec_send_frame(ffmpeg->codec_ctx, ffmpeg->filter_frame);
            if (ret < 0) {
                msg = "video_encoder_write_frame: avcodec_send_frame failed\n";
                ret = ERROR_ENCODER;
                goto error;
            }
            av_frame_unref(ffmpeg->filter_frame);
        }
        av_frame_unref(frame);
        // 重新初始化输入数据帧
        ffmpeg->input_frame->format = ffmpeg->codec_ctx->pix_fmt;
        ffmpeg->input_frame->width = encoder->src_width;
        ffmpeg->input_frame->height = encoder->src_height;
        ret = av_frame_get_buffer(ffmpeg->input_frame, 0);
        if (ret < 0) {
            msg = "video_encoder_write_frame: 分配输入数据帧空间失败\n";
            ret = ERROR_ENCODER;
            goto error;
        }
    } else {
        // 直接将数据传给编码器
        ret = avcodec_send_frame(ffmpeg->codec_ctx, frame);
        if (ret < 0) {
            msg = "video_encoder_write_frame: avcodec_send_frame failed\n";
            ret = ERROR_ENCODER;
            goto error;
        }
    }

    return SUCCESS;

    // 错误
    error:
    LOGE(TAG, "%s", msg);
    return ret;
}

/**
 * 读取编码后的视频数据
 *
 * @param encoder   编码器结构体指针
 * @param data      接收数据的数组
 * @param offset    数组开始位置
 * @return          读取数据的长度，如果数字小于0说明发生错误；如果数字为0,说明没有更多数据了。
 */
int video_encoder_read_frame(
        TcVideoEncoder *encoder,
        uint8_t *data,
        int offset
) {
    int ret;
    char *msg;

    FFmpegTools *ffmpeg = (FFmpegTools *) encoder->extra;
    AVPacket *packet = ffmpeg->output_packet;

    // 读取数据
    ret = avcodec_receive_packet(ffmpeg->codec_ctx, packet);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        // 没有更多数据
        return 0;
    } else if (ret < 0) {
        msg = "video_encoder_write_frame: avcodec_receive_packet failed\n";
        ret = ERROR_ENCODER;
        goto error;
    }
    // 输出数据
    int data_len = packet->size;
    memcpy(data + offset, packet->data, data_len);
    av_packet_unref(packet);
    return data_len;

    // 错误
    error:
    LOGE(TAG, "%s", msg);
    return ret;
}

/**
 * 排空视频编码器数据
 *
 * @param encoder   编码器结构体指针
 * @return          状态码
 */
int video_encoder_flush(
        TcVideoEncoder *encoder
) {
    int ret;
    char *msg;

    FFmpegTools *ffmpeg = (FFmpegTools *) encoder->extra;

    // 将数据传给编码器
    ret = avcodec_send_frame(ffmpeg->codec_ctx, NULL);
    if (ret < 0) {
        msg = "video_encoder_write_frame: avcodec_send_frame failed\n";
        ret = ERROR_ENCODER;
        goto error;
    }

    return SUCCESS;

    // 错误
    error:
    LOGE(TAG, "%s", msg);
    return ret;
}

/**
 * 释放视频编码器
 *
 * @param encoder   编码器结构体指针地址
 * @return          状态码
 */
int video_encoder_release(
        TcVideoEncoder **encoder
) {
    if (!(*encoder)) {
        return SUCCESS;
    }
    // 释放FFmpeg结构体
    FFmpegTools *ffmpeg = (FFmpegTools *) (*encoder)->extra;
    if (ffmpeg) {
        release_ffmpeg(ffmpeg);
    }
    // 释放编码器结构体
    free(*encoder);
    (*encoder) = NULL;

    return SUCCESS;
}